import { Injectable } from '@nestjs/common';
import { LoggerService } from '@/common/logger/logger.service';
import { FileCreateUploadTokenDto } from '@/module/file/file.dto';
import OSS from 'ali-oss';
import dayjs from 'dayjs';
import { ConfigService } from '@nestjs/config';
import { BizFailedException } from '@/common/biz-failed/biz-failed.exception';
import { BizFailedCodeEnum } from '@/common/biz-failed/biz-failed.enum';
import { FileGroup } from 'common/utils/data';
import path from 'path';
import { genTimeWithRandomStr } from 'common/utils/utils';

@Injectable()
export class FileService {
  private readonly client: OSS;

  constructor(
    private readonly loggerService: LoggerService,
    private readonly configService: ConfigService,
  ) {
    this.client = new OSS({
      region: 'oss-cn-shenzhen', // 示例：'oss-cn-hangzhou'，填写Bucket所在地域。
      accessKeyId: this.configService.get<string>('ALIYUN_ACCESS_KEY_ID')!, // 确保已设置环境变量OSS_ACCESS_KEY_ID。
      accessKeySecret: this.configService.get<string>('ALIYUN_ACCESS_KEY_SECRET')!, // 确保已设置环境变量OSS_ACCESS_KEY_SECRET。
      bucket: 'huihifi', // 示例：'my-bucket-name'，填写存储空间名称。
      endpoint: this.configService.get<string>('ALIYUN_OSS_ENDPOINT') || undefined,
    });
  }

  /*async create(file: Express.Multer.File, group: string): Promise<string> {
    const key = `${group}/${nanoid()}.${mime.getExtension(file.mimetype)}`;
    await this.uploadByBufferAndFilename(file.buffer, key);
    return key;
  }

  async uploadByBufferAndFilename(buffer: Buffer, filename: string) {
    const uploadToken = this.qiniuer.putPolicy.uploadToken(this.qiniuer.mac);
    await new Promise((resolve, reject) => {
      this.qiniuer.formUploader.put(
        uploadToken,
        filename,
        buffer,
        this.qiniuer.putExtra,
        (respErr, respBody, respInfo) => {
          if (respErr) {
            this.loggerService.error(respErr);
            reject(respErr);
          }
          if (respInfo.statusCode === 200) {
            resolve(respBody);
          } else {
            this.loggerService.error(respBody);
            reject(respBody);
          }
        },
      );
    });
  }*/

  async createUploadToken(param: FileCreateUploadTokenDto){
    const key = this.genKey(param.group, param.filename);

    const conditions = [
      {"bucket": "huihifi" },
      ["content-length-range", 1, 1024 * 1024 * 200],
      ["eq", "$key", key],
    ];

    const postObject = this.client.calculatePostSignature({
      expiration: dayjs().add(10, 'minutes').toISOString(),
      conditions,
    })

    return {
      ...postObject,
      key,
    }
  }

  async download(param: {key: string;downloadPath: string}){
    try {
      await this.client.get(param.key, param.downloadPath);
    }catch (e){
      throw new BizFailedException(BizFailedCodeEnum.FILE_DOWNLOAD_ERROR);
    }
  }

  async batchDownload(param: {
    list: {key: string;downloadPath: string}[],
  }){
    try {
      await Promise.all(param.list.map(item=>{
        return this.download(item);
      }))
    }catch (e){
      throw new BizFailedException(BizFailedCodeEnum.FILE_DOWNLOAD_ERROR);
    }
  }

  async upload(param: {group?: FileGroup, filePath: string, key?:string}){
    await this.client.put(
      param.key ?? this.genKey(param.group!, path.basename(param.filePath)),
      param.filePath,
    );
  }

  async batchUpload(param: {
    list: {group?: FileGroup, filePath: string, key?:string}[],
  }){
    await Promise.all(param.list.map(item=>{
      return this.upload(item);
    }))
  }

  async uploadLargeFile(param: {group?: FileGroup, filePath: string, key?:string}){
    await this.client.multipartUpload(
      param.key ?? this.genKey(param.group!, path.basename(param.filePath)),
      param.filePath,
      {
        progress: (e)=>{
          this.loggerService.log(`上传进度: ${e}`);
        },
        partSize: 1000 * 1024, //设置分片大小
        timeout: 120000, //设置超时时间
      }
    );
  }

  async batchUploadLargeFile(param: {
    list: {group?: FileGroup, filePath: string, key?:string}[],
  }){
    await Promise.all(param.list.map(item=>{
      return this.uploadLargeFile(item);
    }))
  }

  genKey(
    group: string,
    filename: string,
  ): string {
    return `${group}/${genTimeWithRandomStr()}/${filename}`;
  }

  batchRemoveFile(list: string[]){
    return this.client.deleteMulti(list, { quiet: true });
  }

}
